﻿using com.iot.core.annotation;
using com.iot.core.data;
using com.iot.core.data.entity;
using com.iot.core.exception;
using com.iot.core.infrastructure;
using com.iot.core.manager.htcls;
using com.iot.core.manager.htctl;
using com.iot.core.manager.id;
using com.iot.core.util;
using Microsoft.AspNetCore.JsonPatch.Operations;
using Microsoft.EntityFrameworkCore.Metadata;
using NLog.Fluent;
using Org.BouncyCastle.Ocsp;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Policy;
using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace com.iot.core.manager.htctl
{
    public class HtCtlManager
    {
        [MethodImpl(MethodImplOptions.Synchronized)]
        static HtCtlManager Create()
        {
            //create NopEngine as engine
            return Singleton<HtCtlManager>.Instance ?? (Singleton<HtCtlManager>.Instance = new HtCtlManager());
        }
        public static HtCtlManager Current
        {
            get
            {
                if (Singleton<HtCtlManager>.Instance == null)
                {
                    Create();
                }

                return Singleton<HtCtlManager>.Instance;
            }
        }

        #region 扫描数据
        //region 扫描本地packge
        List<HtCtlDto> lCtlAll;
        List<HtCtlActDto> lActAll;

        public async Task StartAsnyc()
        {
            lCtlAll = new List<HtCtlDto>();

            var typeFinder = Singleton<ITypeFinder>.Instance;
            var _NopDBStartup = typeFinder
                .FindClassesOfAttribute<TagAttribute>()
                .Select(startup => startup)
                .ToList();
            foreach (var m in _NopDBStartup)
            {
                var mcls = GetHtClsDto(m);
                if (mcls != null)
                {
                    lCtlAll.Add(mcls);
                }
            }

            var l1 = lCtlAll.SelectMany(a => a.Actions)
                .ToList(); 
            lActAll = l1;
        }
        HtCtlDto GetHtClsDto(Type clazz)
        {
            var mcls = new HtCtlDto
            {
                Ename = clazz.Name,
                Clazz = clazz,                 
                Memo = "",
                Name = clazz.Name,
                FullName = clazz.FullName,
                Route = "[controller]/[action]",
                Id = 0
            };

            var apiModel = clazz.GetCustomAttribute<TagAttribute>();
            if (apiModel != null)
            {
                mcls.Name = apiModel.Name;
                mcls.Memo = apiModel.Description;
            }

            var mRouteAttribute = clazz.GetCustomAttribute<Microsoft.AspNetCore.Mvc.RouteAttribute>();
            if (mRouteAttribute != null)
            {
                mcls.Route = mRouteAttribute.Template.ToLower();
            }
           
            GetHtCtlActDto(mcls);
            return mcls;
        }

        void GetHtCtlActDto(HtCtlDto mcls)
        {
            var controller = mcls.Clazz.Name.ToLower();
            if (controller.EndsWith("controller"))
            {
                controller = controller.Substring(0, controller.Length - 10);
            }

            var t = mcls.Clazz;

            var r = new List<HtCtlActDto>();
            mcls.Actions = r;


            var methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);
            foreach (var p in methods)
            {
                var apiModel = p.GetCustomAttribute<iot.core.annotation.OperationAttribute>();
                if (apiModel == null)
                {
                    continue;
                }
                var c = r.Where(a => a.Name == p.Name).FirstOrDefault();
                if (c != null)
                {
                    continue;
                }
                var f = new HtCtlActDto
                {
                    Ename = p.Name,
                    Name = apiModel.Summary,
                    Memo = apiModel.Description,
                    AuthType = AuthType.None,
                    FullUrl = "",
                    LogNeed = false,
                    LogPin = true,
                    LogPout = true,
                    LogTemplete = "{ActionName} {Pin.Name}",
                    PinType = "",
                    PoutType = "",
                    Minfo = p,
                    CtlFullName = mcls.FullName,
                    PinName = "",
                    Id = 0,
                    PinIslist = false,
                    PoutIslist = false,
                    PoutIspage = false,

                };

                if (f.Ename.EndsWith("Async"))
                {
                    f.Ename = f.Ename.Substring(0, f.Ename.Length - 5);
                }
                if (f.Ename.StartsWith("Delete"))
                {
                    f.LogTemplete = "{ActionName} {Deleted.Name}";
                }


                var mLogAttribute = p.GetCustomAttribute<iot.core.annotation.LogAttribute>();
                if (mLogAttribute != null)
                {
                    f.LogNeed = mLogAttribute.LogNeed;
                    f.LogPin = mLogAttribute.LogPin;
                    f.LogPout = mLogAttribute.LogPout;
                    if (!string.IsNullOrEmpty(mLogAttribute.LogTemplete))
                    {
                        f.LogTemplete = mLogAttribute.LogTemplete;
                    }
                }

                var mAuthAttribute = p.GetCustomAttribute<iot.core.annotation.AuthAttribute>();
                if (mAuthAttribute != null)
                {
                    f.AuthType = mAuthAttribute.AuthType;
                }

                f.FullUrl = mcls.Route.Replace("[controller]", controller)
                   .Replace("[action]", f.Ename);
                if (!f.FullUrl.StartsWith("/"))
                {
                    f.FullUrl = "/" + f.FullUrl;
                }
                var indexP = f.FullUrl.IndexOf("{");
                if (indexP > 0)
                {
                    //解决 /{id?}格式
                    f.FullUrl = f.FullUrl.Substring(0, indexP);
                }
                f.FullUrl = f.FullUrl.ToLower();


                var ps = p.GetParameters();
                foreach (var mps in ps)
                {
                    if (f.PinName == "")
                    {
                        SetPinType(f, mps);
                        //第一个参数
                    }
                    var mFromBodyAttribute = mps.GetCustomAttribute<Microsoft.AspNetCore.Mvc.FromBodyAttribute>();
                    if (mFromBodyAttribute == null)
                    {
                        continue;
                    }
                    SetPinType(f, mps);
                }

                if (p.ReturnType != null)
                {
                    var pty = p.ReturnType;
                    if (pty.IsGenericType)
                    {
                        if (pty.GetGenericTypeDefinition() == typeof(Task<>))
                        {
                            pty = pty.GetGenericArguments()[0];
                        }
                    }

                    f.PoutType = pty.FullName;
                    if (pty.IsGenericType)
                    {



                        if (pty.GetGenericTypeDefinition() == typeof(List<>) ||
                            pty.GetGenericTypeDefinition() == typeof(IEnumerable<>)
                            )
                        {
                            f.PoutType = pty.GetGenericArguments()[0].FullName;
                            f.PoutIslist = true;
                        }
                        else if (pty.GetGenericTypeDefinition() == typeof(Page<>)
                            )
                        {
                            f.PoutType = pty.GetGenericArguments()[0].FullName;
                            f.PoutIspage = true;
                        }
                    }
                }
                r.Add(f);

            } 
        }
        void SetPinType(HtCtlActDto f, ParameterInfo mps)
        {
            f.PinName = mps.Name;
            f.PinType = mps.ParameterType.FullName;
            if (mps.ParameterType.IsGenericType)
            {
                if (mps.ParameterType.GetGenericTypeDefinition() == typeof(List<>))
                {
                    f.PinType = mps.ParameterType.GetGenericArguments()[0].FullName;
                    f.PinIslist = true;
                }
            } 
        }
        #endregion

        #region 同步数据库
        HtCodeLog CreateHtCodeLogByDto(HtCtl mdb)
        {
            var mcodelog = new HtCodeLog();
            mcodelog.Id = HtIdManager.Current.NextLong();
            mcodelog.Bid = mdb.Id;
            mcodelog.CrtDt = DateTime.Now;
            mcodelog.FullName = mdb.CtlFullName;
            mcodelog.Lx = "控制器";
            mcodelog.Memo = "";
            mcodelog.Name = mdb.Name;
            mcodelog.PrjName = mdb.PrjName;
            return mcodelog;
        }
        HtCodeLog CreateHtCodeLogByDto(HtAction mdb)
        {
            var mcodelog = new HtCodeLog();
            mcodelog.Id = HtIdManager.Current.NextLong();
            mcodelog.Bid = mdb.Id;
            mcodelog.CrtDt = DateTime.Now;
            mcodelog.FullName = mdb.CtlFullName;
            mcodelog.Lx = "API方法";
            mcodelog.Memo = "";
            mcodelog.Name = mdb.Name;
            mcodelog.PrjName = mdb.PrjName;
            return mcodelog;
        }


        List<HtCtl> ldbctl = null;
        List<HtAction> ldbact = null;

        List<HtCls> ldbcls = null;
        string prjName = "";

        List<HtCtlDto> lprjctl = null;
        List<HtCtlActDto> lprjact = null;
        IRepository<HtCtl> htCtlMapper = null;
        IRepository<HtCls> htClsMapper = null;
        IRepository<HtAction> htActionMapper = null;
        IRepository<HtCodeLog> htCodeLogMapper = null;
        public void RefreshToDb(string prjName)
        {
            this.prjName = prjName;
            htClsMapper = EngineContext.Current.Resolve<IRepository<HtCls>>();
            htCtlMapper = EngineContext.Current.Resolve<IRepository<HtCtl>>();
            htActionMapper = EngineContext.Current.Resolve<IRepository<HtAction>>();
            htCodeLogMapper = EngineContext.Current.Resolve<IRepository<HtCodeLog>>();

            lprjctl = lCtlAll.Where(a => a.FullName.StartsWith(prjName)).ToList();
            lprjact = lActAll.Where(a => a.CtlFullName.StartsWith(prjName)).ToList();


            ldbcls = htClsMapper.GetAll().ToList();
            ldbctl = htCtlMapper.GetAll(true).Where(a => a.PrjName.Equals(prjName)).ToList();
            ldbact = htActionMapper.GetAll(true).Where(a => a.PrjName.Equals(prjName)).ToList();

            RefreshCtlToDB();
            RefreshActionToDB();

            htCtlMapper.SaveChagnes();

            ldbcls = null;
            ldbctl = null;
            ldbact = null;

            lprjctl = null;
            lprjact = null;

            htCtlMapper = null;
            htActionMapper = null;
            htCodeLogMapper = null;
        }

        void RefreshCtlToDB()
        {

            foreach(var mcls in lprjctl)
            {
                DealCtrl(mcls);
            }
            foreach (var mdb in ldbctl)
            {
                if (mdb.Deleted)
                {
                    continue;
                }
                var mold = lprjctl.Where(a=>a.FullName.Equals(mdb.CtlFullName))
                        .FirstOrDefault();

                if (mold==null)
                {
                    htCtlMapper.Delete(mdb, true);
                }
            }
        }
        void DealCtrl(HtCtlDto mcls)
        {
            var mdb = ldbctl.Where(a => a.CtlFullName == mcls.FullName).FirstOrDefault();
            if (mdb == null)
            {
                mdb = new HtCtl
                {
                    Id =   HtIdManager.Current.NextLong(),
                    PrjName = prjName,
                    CtlFullName = mcls.FullName,
                    Name = mcls.Name,
                    Memo = mcls.Memo,
                    Route = mcls.Route,

                    CrtDt = DateTime.Now,
                    EdtDt = DateTime.Now,
                    Deleted = false,                     
                };

                try
                {
                    htCtlMapper.Insert(mdb,true);
                }
                catch (Exception ex)
                {
                    throw new MyException("插入HTCtrl错误：" + mdb.CtlFullName + " id:" + mdb.Id);
                }
            }
            else
            {
                if (mdb.Name != mcls.Name)
                {
                    mdb.Name = mcls.Name;
                }
                if (mdb.Memo != mcls.Memo)
                {
                    mdb.Memo = mcls.Memo;
                }
                if (mdb.Route != mcls.Route)
                {
                    mdb.Route = mcls.Route;
                }
                if (mdb.Deleted)
                {
                    mdb.Deleted = false;
                }
            }
        }
        void RefreshActionToDB()
        {
            foreach(var mcls  in lprjact)
            {
                DealAction(mcls);
            }
            foreach(var mdb in ldbact)
            {
                if (mdb.Deleted)
                {
                    continue;
                }
                var mold = lprjact.Where(a => a.CtlFullName.Equals(mdb.CtlFullName)
                    && a.Ename.Equals(mdb.Ename)).FirstOrDefault();                       
                if (mold==null)
                {
                    htActionMapper.Delete(mdb, true);
                    var mcodelog = CreateHtCodeLogByDto(mdb);
                    mcodelog.Memo="删除";
                    htCodeLogMapper.Insert(mcodelog);
                }
            }
        }
        void DealAction(HtCtlActDto mcls)
        {
            var mdb = ldbact.Where(a => a.CtlFullName.Equals(mcls.CtlFullName)
                    && a.Ename.Equals(mcls.Ename)).FirstOrDefault();
            if (mdb==null)
            {
                mdb = new HtAction()
                {
                    Id = HtIdManager.Current.NextLong(),
                    PrjName = prjName,
                    CtlFullName = mcls.CtlFullName,
                    FullUrl = mcls.FullUrl,
                    Name = mcls.Name,
                    Ename = mcls.Ename,
                    Memo = mcls.Memo,
                    PinType = mcls.PinType,
                    PinIslist = mcls.PinIslist,
                    PoutType = mcls.PoutType,
                    PoutIslist = mcls.PoutIslist,
                    PoutIspage = mcls.PoutIspage,
                    AuthType = mcls.AuthType.ToString(),
                    LogNeed = mcls.LogNeed,
                    LogTemplete = mcls.LogTemplete,
                    LogPin = mcls.LogPin,
                    LogPout = mcls.LogPout,

                    CrtDt = DateTime.Now,
                    EdtDt = DateTime.Now,
                    Deleted = false
                };                 
                htActionMapper.Insert(mdb);

                var mcodelog = CreateHtCodeLogByDto(mdb);
                mcodelog.Memo="新增";
                htCodeLogMapper.Insert(mcodelog);
            }
            else
            {           
                var msg = CompareActionField(mcls, mdb);
                if (mdb.Deleted )
                {
                    mdb.Deleted = false;
                    msg = "删除恢复\r\n" + msg;
                }

                if (!"".Equals(mdb.PinType))
                {
                    var mcls2 = ldbcls.Where(a => a.FullName.Equals(mdb.PinType)).FirstOrDefault();
                    if (mcls2!=null)
                    {                         
                        if (mcls2.EdtDt > mdb.EdtDt)
                        {
                            msg += string.Format("参数类型 {0} 更新", mcls2.FullName );
                        }
                    } 
                }
                if (!"".Equals(mdb.PoutType))
                {
                    var mcls2 = ldbcls.Where(a => a.FullName.Equals(mdb.PoutType )).FirstOrDefault();
                    if (mcls2 != null)
                    {
                        if (mcls2.EdtDt > mdb.EdtDt)
                        {
                            msg += string.Format("返回结果类型 {0} 更新", mcls2.FullName);
                        }
                    }
                }

               
                if (!"".Equals(msg))
                {
                    htActionMapper.Update(mdb, true);
                    var mcodelog = CreateHtCodeLogByDto(mdb);
                    mcodelog.Memo = msg; 
                    htCodeLogMapper.Insert(mcodelog, true);
                }

            }
        }
        string CompareAction(HtCtlActDto mcls, HtAction mdb, string fieldName)
        {
            var v = HtClsManager.Current.GetFieldStrV(mcls, fieldName);
            var dbv = HtClsManager.Current.GetFieldStrV(mdb, fieldName);
            if (!v.Equals(dbv))
            { 
                HtClsManager.Current.SetFieldV(mdb, fieldName, v);
                return fieldName + " " + dbv + "=>" + v + "\r\n";
            }
            return "";
        }
        string CompareActionField(HtCtlActDto mcls, HtAction mdb)
        {
            var sb = new StringBuilder();
            sb.Append(CompareAction(mcls, mdb, "CtlFullName"));
            sb.Append(CompareAction(mcls, mdb, "FullUrl"));
            sb.Append(CompareAction(mcls, mdb, "Name"));
            sb.Append(CompareAction(mcls, mdb, "Memo"));
            sb.Append(CompareAction(mcls, mdb, "PinType"));
            sb.Append(CompareAction(mcls, mdb, "PinIslist"));
            sb.Append(CompareAction(mcls, mdb, "PoutType"));
            sb.Append(CompareAction(mcls, mdb, "PoutIslist"));
            sb.Append(CompareAction(mcls, mdb, "PoutIspage"));
            sb.Append(CompareAction(mcls, mdb, "AuthType"));
            sb.Append(CompareAction(mcls, mdb, "LogNeed"));
            sb.Append(CompareAction(mcls, mdb, "LogTemplete"));
            sb.Append(CompareAction(mcls, mdb, "LogPin"));
            sb.Append(CompareAction(mcls, mdb, "LogPout"));
            return sb.ToString();
        }
        #endregion

        #region 公共方法
        public HtCtlDto getCtl(string fullname)
        {
            var m = lCtlAll.Where(a => a.FullName.Equals(fullname)).FirstOrDefault(); 
            return m;
        }
        public HtCtlActDto GetAction(string url)
        {
            url = url.ToLower();
            var m = lActAll.Where(a => a.FullUrl.Equals(url)).FirstOrDefault();
            return m; 
        }

        public HtCtlActDto GetAction(string ctlFullName, string ename)
        {
            var m = lActAll.Where(a => a.CtlFullName.Equals(ctlFullName)
                &&a.Ename.Equals(ename)
            ).FirstOrDefault();
            return m;
        }

        #endregion

    }
}
